package net.zjitc.config;

import net.zjitc.filter.JwtAuthenticationFilter;
import net.zjitc.security.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @author 毛钰深
 * @create 2022/1/23
 */
@Configuration
/*开启security的相关安全规则的配置
 加载了WebSecurityConfiguration配置类, 配置安全认证策略
 加载了AuthenticationConfiguration, 配置了认证信息
*/
//@EnableWebSecurity
/*开启springboot方法级安全
prePostEnabled = true 会解锁 @PreAuthorize 和 @PostAuthorize 两个注解
从名字就可以看出@PreAuthorize 注解会在方法执行前进行验证(常用)
而 @PostAuthorize 注解会在方法执行后进行验证
 */
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String[] URL_WHITELIST={
            "/login",
            "/logout",
            "/captcha",
            "/upload/**",
    };

    @Autowired
    private LoginFailureHandler loginFailureHandler;

    @Autowired
    private LoginSuccessHandler loginSuccessHandler;

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private JwtAccessDeniedHandler jwtAccessDeniedHandler;

    @Autowired
    //UserDetails实现类
    private IUserDetailService iUserDetailService;

    @Autowired
    private JwtLogoutSuccessHandler jwtLogoutSuccessHandler;

    @Bean
    //创建jwt认证器的Bean对象到容器中
    JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        return new JwtAuthenticationFilter(authenticationManager());
    }

    @Bean
    //配置密码加密的方式
    BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    UsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception {
        MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter=new MyUsernamePasswordAuthenticationFilter();
        myUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManagerBean());
        //成功处理器
        myUsernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(loginSuccessHandler);
        //失败处理器
        myUsernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(loginFailureHandler);
        return myUsernamePasswordAuthenticationFilter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //开启跨域配置以及关闭csrf(防止攻击)
        http.cors().and()
                .csrf().disable()
        //登录配置(成功和失败)
        .formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
        .and()
                //登出配置
                .logout()
                .logoutSuccessHandler(jwtLogoutSuccessHandler)
        .and()
                //禁用session,获取session管理器
                .sessionManagement()
                //配置session的生成策略为不生成session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        .and()
                //配置拦截规则,配置白名单,其余拦截
                .authorizeRequests()
                //白名单直接跳过拦截(放行)
                .antMatchers(URL_WHITELIST).permitAll()
                //所有的请求全部拦截,并且必须登录之后才可以被访问
                .anyRequest().authenticated()
        .and()
                //配置权限异常处理器
                .exceptionHandling()
                //配置未认证处理器的入口
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                //权限不足处理器
                .accessDeniedHandler(jwtAccessDeniedHandler)
        .and()
                //配置自定义的过滤器

                //添加自定义过滤器
                .addFilter(jwtAuthenticationFilter())
        ;

        //使用自定义json的密码拦截器
        http.addFilterAt(myUsernamePasswordAuthenticationFilter(),UsernamePasswordAuthenticationFilter.class);
    }

    /**
     * 配置自定义的UserDetails
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(iUserDetailService);
    }
}
